using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Tool
{
    class EventTimeEditor
    {
        private System.Windows.Forms.Form m_parent;
        private int m_grabbedIndx;
        private int m_grabbedOffset;
        private int m_selectedIndx;
        private int m_eventTotalTime; //total event time in miliseconds

        const float secMult = 1000;
        const int soundHeight = 50;
        const int vertGap = 5;
        const float rectThickness = 3.0f;
        const int topOffset = 2;
        const int btmOffset = 1;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        public EventTimeEditor(System.Windows.Forms.Form parent)
        {
            m_parent = parent;
            m_grabbedIndx = -1;
            m_grabbedOffset = 0; //offset in pixel space
            m_selectedIndx = -1;

            m_eventTotalTime = 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private ArrayList GetActiveSndList()
        {
            string evntName = "";

            if (((Editor)m_parent).EventTree.SelectedNode != null)
            {
                if (((Editor)m_parent).EventTree.SelectedNode.Parent == null)
                {
                    evntName = ((Editor)m_parent).EventTree.SelectedNode.Text;
                }
                else
                {
                    evntName = ((Editor)m_parent).EventTree.SelectedNode.Parent.Text;
                }
            }

            return (((Editor)m_parent).GetEventTree().GetEventSounds(evntName));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private float CalculateTotalEventTime()
        {
            ArrayList soundList = GetActiveSndList();
              // figure out how long this event plays for
            m_eventTotalTime = 0;
            for (int i = 0; i < soundList.Count; ++i)
            {
                EventSound sound = ((EventSound)soundList[i]);


                int sndLength = 0;
                if( ((Editor)m_parent).m_soundLibrary.GetFileByFilename(sound.GetName()) != null )
                    sndLength = ((Editor)m_parent).m_soundLibrary.GetFileByFilename(sound.GetName()).GetFileLength();

                int sndTimeEnd = sound.GetTimeOffset() + sndLength;

                if (sndTimeEnd > m_eventTotalTime)
                {
                    m_eventTotalTime = sndTimeEnd;
                }
            }

            //((Editor)m_parent).TimeTrackBar.TickFrequency = (int)(((Editor)m_parent).TimeTrackBar.Maximum / m_eventTotalTime);
            ((Editor)m_parent).TotalTimeTextBox.Text = string.Format("{0:N2}s", m_eventTotalTime / secMult);

            return m_eventTotalTime;
        }

        /// <summary>
        /// 
        /// </summary>
        public void UpdateScroll()
        {
            ArrayList soundList = GetActiveSndList();

            ((Editor)m_parent).EventTimeScroll.SmallChange = ((Editor)m_parent).EventTimePanel.Height/20;
            ((Editor)m_parent).EventTimeScroll.LargeChange = ((Editor)m_parent).EventTimePanel.Height;

            int totalHeight = soundList.Count * soundHeight + btmOffset + 2 * (int)rectThickness +
                (soundList.Count-1)*2*(int)rectThickness + (soundList.Count-1)*vertGap;
            if (totalHeight > ((Editor)m_parent).EventTimePanel.Height)
            {
                ((Editor)m_parent).EventTimeScroll.Enabled = true;
                ((Editor)m_parent).EventTimeScroll.Maximum = totalHeight;

                int maxVal = ((Editor)m_parent).EventTimeScroll.Maximum - ((Editor)m_parent).EventTimeScroll.LargeChange + 1;
                if (((Editor)m_parent).EventTimeScroll.Value > maxVal)
                {
                    ((Editor)m_parent).EventTimeScroll.Value = maxVal;
                }
            }
            else
            {
                ((Editor)m_parent).EventTimeScroll.Enabled = false;
                ((Editor)m_parent).EventTimeScroll.Value = 0;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gfx"></param>
        public void Draw( Graphics gfx )
        {
            ((Editor)m_parent).TotalTimeTextBox.BackColor = Color.White;
            ((Editor)m_parent).TotalTimeTextBox.Enabled = true;
            ((Editor)m_parent).LoopCheckBox.Enabled = true; 

            ArrayList soundList = GetActiveSndList();
            if (((Editor)m_parent).EventTree.SelectedNode == null || soundList.Count == 0)
            {
                  // set the background color
                gfx.Clear(Color.LightGray);
                ((Editor)m_parent).TimeTrackBar.Enabled = false;
                ((Editor)m_parent).TotalTimeTextBox.Enabled = false;
                ((Editor)m_parent).LoopCheckBox.Enabled = false;
                ((Editor)m_parent).LoopCheckBox.Checked = false;
                ((Editor)m_parent).EventLabel.Text = "";

                if (((Editor)m_parent).EventTree.SelectedNode == null)
                {
                    ((Editor)m_parent).TotalTimeTextBox.Text = "";
                    ((Editor)m_parent).TotalTimeTextBox.BackColor = Control.DefaultBackColor;
                }
                else
                {
                    ((Editor)m_parent).TotalTimeTextBox.Text = "0.00s";

                }

                return;
            }

            if( ((Editor)m_parent).EventTree.SelectedNode.Parent == null )
                ((Editor)m_parent).EventLabel.Text = "Editing Event: \"" + ((Editor)m_parent).EventTree.SelectedNode.Text + "\"";
            else
                ((Editor)m_parent).EventLabel.Text = "Editing Event: \"" + ((Editor)m_parent).EventTree.SelectedNode.Parent.Text + "\"";

            if (((Editor)m_parent).EventTimePanel.Focused)
                gfx.Clear(Color.White);
            else
                gfx.Clear(Color.White);

            ((Editor)m_parent).TimeTrackBar.Enabled = true;
            UpdateScroll();

            CalculateTotalEventTime();
            DrawSecLines(gfx);

              // for each sound in this event
            for (int i = 0; i < soundList.Count; ++i)
            {
                  // draw this sound's visual block representation
                DrawRect( gfx, i, ((EventSound)soundList[i]) );               
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gfx"></param>
        private void DrawSecLines(Graphics gfx)
        {
              // 
            Brush secLineBrush = new System.Drawing.Drawing2D.HatchBrush(
                System.Drawing.Drawing2D.HatchStyle.DashedHorizontal, ((Editor)m_parent).EventTimePanel.Focused ? 
                Color.White : Color.White, Color.LightGreen);
            Pen secLinePen = new Pen(secLineBrush, 1.0f);

            int windWidth = ((Editor)m_parent).EventTimePanel.Width - ((Editor)m_parent).EventTimeScroll.Width;

            float totalSecTime = m_eventTotalTime / secMult;
            for (int s = 0; s < totalSecTime; ++s)
            {
                int secXPos = (int)(s / totalSecTime * windWidth);

                Point pt1 = new Point(secXPos, 0);
                Point pt2 = new Point(secXPos, ((Editor)m_parent).EventTimePanel.Height);

                gfx.DrawLine(secLinePen, pt1, pt2);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gfx"></param>
        /// <param name="sndIndx"></param>
        /// <param name="sound"></param>
        private void DrawRect(Graphics gfx, int sndIndx, EventSound sound)
        {
              // a pen for drawing the sound rectangles
            Color sndRectColor = Color.Black;
            Brush sndRectBrush = new SolidBrush(sndRectColor);
            Pen sndRectPen = new Pen(sndRectBrush, rectThickness);
              // a pen for drawing the grabed sound rectangle
            Color grabSndColor = Color.Red;
            Brush grabSndBrush = new SolidBrush(grabSndColor);
            Pen grabSndPen = new Pen(grabSndBrush, rectThickness);
              // 
            Color fillRectColor = Color.Azure;
            Brush fillSndBrush = new SolidBrush(fillRectColor);
              // 
            Color slctRectColor = Color.Lavender;
            Brush slctUnfocusFillBrush = new SolidBrush(slctRectColor);
            Brush slctFocusFillBrush = new System.Drawing.Drawing2D.HatchBrush(
                System.Drawing.Drawing2D.HatchStyle.BackwardDiagonal, Color.Silver, slctRectColor);

            Color slctFocusColor = Color.Navy;
            Brush sndFocusBrush = new SolidBrush(slctFocusColor);
            Pen sndFocusPen = new Pen(sndFocusBrush, rectThickness);

            float sndLength = 0;
            if( ((Editor)m_parent).m_soundLibrary.GetFileByFilename(sound.GetName()) != null )
                sndLength = ((Editor)m_parent).m_soundLibrary.GetFileByFilename(sound.GetName()).GetFileLength();
            int windWidth = ((Editor)m_parent).EventTimePanel.Width - ((Editor)m_parent).EventTimeScroll.Width;

            float offsetInSec = sound.GetTimeOffset() / secMult;
            float totalTimeInSec = m_eventTotalTime / secMult;

            int width = (int)( (windWidth - 2 * rectThickness) * sndLength / (float)m_eventTotalTime);
            int y = (soundHeight + vertGap + 2*(int)rectThickness ) * sndIndx - ((Editor)m_parent).EventTimeScroll.Value;
            int x = (int)( sound.GetTimeOffset() / (float)m_eventTotalTime * windWidth);

            if (sound.GetTimeOffset() + sndLength == m_eventTotalTime)
                width = windWidth - x - 2 * (int)rectThickness;

            width = m_eventTotalTime > 0.0 ? width : 0;
            x = m_eventTotalTime > 0.0 ? x : 0;

            //if (sndIndx == m_selectedIndx)
            //    ((Editor)m_parent).label1.Text = sound.GetVarName(EDIT_TYPE.EDIT_VOLUME);
            
              // create a rect for representing this sound
            Rectangle sndRect = new Rectangle(x, y, width, soundHeight);

            if (sndIndx == m_selectedIndx && ((Editor)m_parent).EventTimePanel.Focused )
                gfx.FillRectangle(slctFocusFillBrush, sndRect);
            else if( sndIndx == m_selectedIndx )
                gfx.FillRectangle(slctFocusFillBrush, sndRect);
            else
                gfx.FillRectangle(fillSndBrush, sndRect);

            DrawGraphVals(gfx, x, y, width, sound);

              // draw either a selected sound block or 
            if (sndIndx == m_grabbedIndx)
                gfx.DrawRectangle(grabSndPen, sndRect);
            else if (sndIndx == m_selectedIndx && ((Editor)m_parent).EventTimePanel.Focused)
                gfx.DrawRectangle(sndRectPen, sndRect);
            else
                gfx.DrawRectangle(sndRectPen, sndRect);

            const int txtOffset = 3;
            Font sndFont = new Font("Microsoft Sans Serif", 8.0f);
              // draw a string labeling this sound block
            string strLength = string.Format("{0:N2}", sndLength / secMult );
            string strStart  = string.Format("{0:N2}", sound.GetTimeOffset() / secMult );
            string strEnd    = string.Format("{0:N2}", (sound.GetTimeOffset() + sndLength) / secMult );

            string sndLabel = sound.GetName() + " : " + strLength + "s\nstart: " + 
                strStart + "s, stop: " + strEnd + "s";
            gfx.DrawString(sndLabel, sndFont, sndRectBrush, x + txtOffset, y + txtOffset);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="width"></param>
        private void DrawGraphVals(Graphics gfx, int x, int y, int width, EventSound sound)
        {
            // get the colors for drawing volume graphlines
            Color volColor = sound.GetVolumeModifier().GetActiveColor();
            Brush volBrush = new SolidBrush(volColor);
            // get the colors for drawing pan graphlines
            Color panColor = sound.GetPanModifier().GetActiveColor();
            Brush panBrush = new SolidBrush(panColor);
            // get the colors for drawing distortion graphlines
            Color distorColor = sound.GetDistModifier().GetActiveColor();
            Brush distorBrush = new SolidBrush(distorColor);
            // get the colors for drawing reverb graphlines
            Color revColor = sound.GetReverbModifier().GetActiveColor();
            Brush revBrush = new SolidBrush(revColor);

            int graphW = width - 2 * (int)rectThickness;
            int graphH = soundHeight - 2 * (int)rectThickness;
            int graphX = x + (int)rectThickness;
            int graphY = y + (int)rectThickness;


            int graphIndx = ((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex < 0 ?
                1 : (((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex + 1) % 4;
            //graphIndx = graphIndx < 0 ? 1 : graphIndx;
            for (int graphCount = 0; graphCount < 4 && graphIndx >= 0; ++graphCount)
            {
                ArrayList ptList = new ArrayList();
                Brush brush = new SolidBrush(Color.Transparent);

                switch (graphIndx)
                {
                    case 0:
                        brush = panBrush;
                        ptList = sound.GetPanModifier().GetEditPoints(); break;
                    case 1:
                        brush = volBrush;
                        ptList = sound.GetVolumeModifier().GetEditPoints(); break;
                    case 2:
                        brush = distorBrush;
                        ptList = sound.GetDistModifier().GetEditPoints(); break;
                    case 3:
                        brush = revBrush;
                        ptList = sound.GetReverbModifier().GetEditPoints(); break;
                }

                Pen pen = new Pen( brush );
                for (int ptIndex = 0; ptIndex < ptList.Count - 1; ++ptIndex)
                {
                    Point pt1 = ((SoundEditorPoint)ptList[ptIndex]).GetCSharpPt(graphW, graphH, graphX, graphY);
                    Point pt2 = ((SoundEditorPoint)ptList[ptIndex + 1]).GetCSharpPt(graphW, graphH, graphX, graphY);

                    gfx.DrawLine(pen, pt1, pt2);
                }

                graphIndx = (graphIndx + 1) % 4;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private int SndIndex(int x, int y)
        {
            ArrayList soundList = GetActiveSndList();

            int i = 0;
            for (; i < soundList.Count; ++i)
            {
                int blockTop = i * soundHeight + topOffset + (i - 1) * 2 * (int)rectThickness + i * vertGap + (int)rectThickness;
                int blockBtm = (i + 1) * soundHeight + btmOffset + i * 2 * (int)rectThickness + i * vertGap;

                int entireListY = y + ((Editor)m_parent).EventTimeScroll.Value;
                if (entireListY >= blockTop && entireListY <= blockBtm)
                    break;
            }

            if (i < soundList.Count)
            {
                //return i;

                  // needed to get this sound's length
                SoundFile snd = ((Editor)m_parent).m_soundLibrary.GetFileByFilename(((EventSound)soundList[i]).GetName());
                int sndLength = 0;
                if( snd != null )
                    sndLength = snd.GetFileLength();

                int windWidth = ((Editor)m_parent).EventTimePanel.Width - ((Editor)m_parent).EventTimeScroll.Width;
                int startX = ((int)(((EventSound)soundList[i]).GetTimeOffset() / (float)m_eventTotalTime * windWidth));
                int endX = startX + ((int)((windWidth - 2 * rectThickness) * sndLength / (float)m_eventTotalTime));

                  // if the mouse is within the sound block
                if (x >= startX - (int)rectThickness && x <= endX + (int)rectThickness)
                {
                    return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void OnLMouseDown( int x, int y )
        {
            int i = SndIndex(x, y);
            m_grabbedIndx = SndIndex(x, y);
            if (m_grabbedIndx >= 0)
            {
                ArrayList soundList = GetActiveSndList();
                int windWidth = ((Editor)m_parent).EventTimePanel.Width - ((Editor)m_parent).EventTimeScroll.Width;
                int startX = ((int)(((EventSound)soundList[m_grabbedIndx]).GetTimeOffset() / (float)m_eventTotalTime * windWidth));

                m_grabbedOffset = x - startX;

                m_selectedIndx = m_grabbedIndx;
                if (((Editor)m_parent).EventTree.SelectedNode.Parent == null)
                {
                    ((Editor)m_parent).EventTree.SelectedNode =
                        ((Editor)m_parent).EventTree.SelectedNode.Nodes[m_selectedIndx];
                }
                else
                {
                    ((Editor)m_parent).EventTree.SelectedNode =
                        ((Editor)m_parent).EventTree.SelectedNode.Parent.Nodes[m_selectedIndx];
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void OnRMouseDown(int x, int y)
        {
            int i = SndIndex(x, y);
            if (i >= 0)
            {
                m_selectedIndx = i;
                if (((Editor)m_parent).EventTree.SelectedNode.Parent == null)
                {
                    ((Editor)m_parent).EventTree.SelectedNode =
                        ((Editor)m_parent).EventTree.SelectedNode.Nodes[m_selectedIndx];
                }
                else
                {
                    ((Editor)m_parent).EventTree.SelectedNode =
                        ((Editor)m_parent).EventTree.SelectedNode.Parent.Nodes[m_selectedIndx];
                }

                ((Editor)m_parent).EventTimePanel.ContextMenuStrip = ((Editor)m_parent).SoundsRightClickMenu;
            }
            else
            {
                ((Editor)m_parent).EventTimePanel.ContextMenuStrip = null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        public void SelectSound(int index)
        {
            if ((m_selectedIndx = index) == -1)
                return;            

            int slctedBtm = (m_selectedIndx + 1) * soundHeight + btmOffset +
                m_selectedIndx * 2 * (int)rectThickness + m_selectedIndx * vertGap;

            int slctedTop = m_selectedIndx * soundHeight + topOffset + (m_selectedIndx - 1) *
                2 * (int)rectThickness + m_selectedIndx * vertGap + (int)rectThickness;
            slctedTop = slctedTop < 0 ? 0 : slctedTop;

            if (slctedBtm + 2 * (int)rectThickness > ((Editor)m_parent).EventTimePanel.Height + ((Editor)m_parent).EventTimeScroll.Value)
            {
                ((Editor)m_parent).EventTimeScroll.Value = slctedBtm + 2 * (int)rectThickness - ((Editor)m_parent).EventTimePanel.Height;
            }
            else if (slctedTop < ((Editor)m_parent).EventTimeScroll.Value)
            {
                ((Editor)m_parent).EventTimeScroll.Value = slctedTop;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void OnLMouseUp()
        {
            m_grabbedIndx = -1;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void OnMouseMove( int x, int y )
        {
            if (m_grabbedIndx < 0)
                return;

            ArrayList sndList = GetActiveSndList();
            EventSound selectedSnd = ((EventSound)sndList[m_grabbedIndx]);

            int windWidth = ((Editor)m_parent).EventTimePanel.Width - ((Editor)m_parent).EventTimeScroll.Width;
            int newOffset = (int)( (x - m_grabbedOffset) / (float)windWidth * m_eventTotalTime );

            if (newOffset >= 0)
                selectedSnd.SetTimeOffset(newOffset);
            else
                selectedSnd.SetTimeOffset(0);
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void OnMouseDblClick(int x, int y)
        {
            //m_selectedIndx = SndIndex(x,y);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        public void ShiftSelectedSnd(int offset)
        {
            if (m_selectedIndx < 0)
                return;

            ArrayList sndList = GetActiveSndList();
            EventSound selectedSnd = ((EventSound)sndList[m_selectedIndx]);

            int newOffset = selectedSnd.GetTimeOffset() + offset;
            newOffset = newOffset < 0 ? 0 : newOffset;

            selectedSnd.SetTimeOffset(newOffset);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="down"></param>
        public void SelectNextSound(bool down)
        {
            ArrayList sndList = GetActiveSndList();
            m_selectedIndx += down ? 1 : -1;

            if (m_selectedIndx >= sndList.Count)
                m_selectedIndx = sndList.Count - 1;
            else if (m_selectedIndx < 0)
                m_selectedIndx = 0;

            SelectSound(m_selectedIndx);

            if( ((Editor)m_parent).EventTree.SelectedNode.Parent == null )
                ((Editor)m_parent).EventTree.SelectedNode = ((Editor)m_parent).EventTree.SelectedNode.Nodes[m_selectedIndx];
            else
                ((Editor)m_parent).EventTree.SelectedNode = ((Editor)m_parent).EventTree.SelectedNode.Parent.Nodes[m_selectedIndx];
        }
    }
}
